package com.hz.tgb.crypto;


import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StopWatch;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/** 文件MD5
 *
 * @author hezhao on 19/01/25
 */
public class FileMD5Util {

    private static final Logger log = LoggerFactory.getLogger(FileMD5Util.class);

    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private static MessageDigest getMessageDigest(String algorithm) {
        try {
            if (StringUtils.isBlank(algorithm)) {
                algorithm = "MD5";
            }
            return MessageDigest.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            log.error("{} 初始化失败，MessageDigest不支持MD5!", FileMD5Util.class.getName(), e);
        }
        return null;
    }

    public static String getFileMD5WithoutException(File file) {
        try {
            return getFileMD5String(file);
        } catch (Throwable e) {
            log.error("计算文件md5失败, {}", file.getName(), e);
            return "";
        }
    }

    /**
     * 获取文件MD5值
     * @param file
     * @return
     * @throws IOException
     */
    public static String getFileMD5String(File file) throws IOException {
        FileInputStream in = new FileInputStream(file);
        FileChannel ch = in.getChannel();

        //700000000 bytes are about 670M
        int maxSize = 700000000;

        long startPosition = 0L;
        long step = file.length() / maxSize;
        MessageDigest messageDigest = getMessageDigest("MD5");

        if (step == 0) {
            MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
            messageDigest.update(byteBuffer);
            return bufferToHex(messageDigest.digest());
        }

        for (int i = 0; i < step; i++) {
            MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, startPosition, maxSize);
            messageDigest.update(byteBuffer);
            startPosition += maxSize;
        }

        if (startPosition == file.length()) {
            return bufferToHex(messageDigest.digest());
        }

        MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, startPosition, file.length() - startPosition);
        messageDigest.update(byteBuffer);

        return bufferToHex(messageDigest.digest());
    }

    /**
     * 获取字符串 MD5值
     * @param s
     * @return
     */
    public static String getMD5String(String s) {
        try {
            return getMD5String(s.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取byte[] MD5值
     * @param bytes
     * @return
     */
    public static String getMD5String(byte[] bytes) {
        MessageDigest messageDigest = getMessageDigest("MD5");
        messageDigest.update(bytes);
        return bufferToHex(messageDigest.digest());
    }

    private static String bufferToHex(byte[] bytes) {
        return bufferToHex(bytes, 0, bytes.length);
    }

    private static String bufferToHex(byte[] bytes, int m, int n) {
        StringBuilder sb = new StringBuilder(2 * n);
        int k = m + n;
        for (int l = m; l < k; l++) {
            appendHexPair(bytes[l], sb);
        }
        return sb.toString();
    }

    private static void appendHexPair(byte bt, StringBuilder sb) {
        char c0 = HEX_DIGITS[(bt & 0xf0) >> 4];
        char c1 = HEX_DIGITS[bt & 0xf];
        sb.append(c0);
        sb.append(c1);
    }

    public static boolean checkPassword(String password, String md5PwdStr) {
        String s = getMD5String(password);
        return s.equals(md5PwdStr);
    }

//    public static void main(String[] args) {
//        try {
//            StopWatch watch = new StopWatch("文件MD5效率测试");
//
//            watch.start("FileMD5Util.getFileMD5String start");
//            String fileMD5String = FileMD5Util.getFileMD5String(new File("C:\\db.txt"));
//            watch.stop();
//            System.out.println(fileMD5String);
//
//            String fileMD5String1 = FileMD5Util.getFileMD5String(new File("C:\\db - 副本.txt"));
//            System.out.println(fileMD5String1);
//
//            // bytes
//            watch.start("FileMD5Util.readFileByBytes start");
//            byte[] data = FileUtil.readFileByBytes("C:\\db.txt");
//            String md5 = FileMD5Util.getMD5String(data);
//            watch.stop();
//            System.out.println(md5);
//
//            System.out.println();
//            System.out.println(watch.prettyPrint());
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//    }

}